截至目前為止,已經可以順利在Offline
的情境下,
正常瀏覽網站內容,也能暫存網站的內容到客戶端的DB,
但如果網站頁面上,很常會有表單的需求,例如,註冊會員、新增產品等等,
即使離線情況下,使用者點選「送出」按鈕,一樣會失敗,
因為沒有網路的情況下是無法向伺服器(Server
)發出請求的,Background Sync
正好可以解決此問題。
既然沒網路情況下,無法將資料傳出去,那麼我們可以將資料先暫存IndexedDB
,並註冊一個Sync Task
,等網路重新連線時,就會出發Service Worker
中的sync
監聽事件,
這時候,再將資料庫中的資料取出來,POST
到伺服器,就可以成功實現離線延遲送出的效果。
開啟post.js
if('serviceWorker' in navigator && 'SyncManager' in window){
...
} else{
...
}
跟Service Worker
一樣,使用前要先檢查SyncManager
是否支援,如果支援就可以使用此功能,如果不存在,就保持原本執行的流程。
navigator.serviceWorker.ready
.then(function(sw){
var form = {
id: new Date().toISOString(),
title: inputTitle.value,
content: inputContent.value,
location: inputLocation.value,
image: '"https://firebasestorage.googleapis.com/v0/b/days-pwas-practice.appspot.com/o/taipei_street.PNG?alt=media&token=8736b68e-216c-4c63-a7d4-a129875ba71e"'
};
writeData('sync-posts', form)
.then(function(){
sw.sync.register('sync-new-post');
})
.then(function(){
console.log('文章以使用Background Sync方式儲存');
})
.catch(function(err){
console.log('Error',err);
});
});
假設環境支援,即可透過navigator.serviceWorker.ready
從DOM
中,操作Service Worker
,成功則回傳Promise
,接著將使用者輸入的內容丟入form
變數中。
writeData('sync-posts', form)
,是我們之前寫的寫入資料的功能,接著將表單寫入sync-posts
之中。
寫入成功後,接著向Service Worker
註冊任務sync-new-post
。
var dbPromise = idb.open('articles', 1, function(db){
if(!db.objectStoreNames.contains('article'))
db.createObjectStore('article', {keyPath: 'id'});
if(!db.objectStoreNames.contains('sync-posts'))
db.createObjectStore('sync-posts', {keyPath: 'id'});
});
在post.js
中,寫道的是要將資料丟入sync-posts
中,因此,在IndexedDB.js
中,如果sync-posts
不存在,就建立資料表。
假如資料表沒建立,資料會因為找不到對應的表而無法存入資料
處理完瀏覽器有支援的情況,接著如果沒有支援,則必須執行原本送出表單的方式。
var articleUrl = 'https://days-pwas-practice.firebaseio.com/article.json';
function sendForm(){
fetch(articleUrl,{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
id: new Date().toISOString(),
title: inputTitle.value,
location: inputLocation.value,
content: inputContent.value,
image: '"https://firebasestorage.googleapis.com/v0/b/days-pwas-practice.appspot.com/o/taipei_street.PNG?alt=media&token=8736b68e-216c-4c63-a7d4-a129875ba71e"'
})
})
.then(function (res) {
console.log('送出表單',res);
getArticleFromDB();
});
}
if('serviceWorker' in navigator && 'SyncManager' in window){}
else{
sendForm();
}
這裡透過fetch
方式,post
資料到firebase
將資料存入。
self.addEventListener('sync', function(event){
console.log('[SW] Background syncing', event);
if(event.tag === 'sync-new-post') {
console.log('抓到TAG-POST 表單');
event.waitUntil(
readAllData('sync-posts')
.then(function(data){
for(var post of data)
{
fetch('https://days-pwas-practice.firebaseio.com/article.json',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
id: post.id,
title: post.title,
location: post.location,
content: post.content,
image: post.image
})
})
.then(function (res) {
console.log('送出表單',res);
if(res.ok){
deleteArticleData('sync-posts',post.id);
}
})
.catch(function(err){
console.log('POST表單失敗!',err);
});
}
})
);
}
});
接著透過event.tag
來比對我們設定的task
,如果一致,則從我們前幾天,寫的readAllData()
功能中,將要post
的內容,用for
迴圈送出到伺服器。
如果表單送出成功,接著要刪除sync-posts
資料表中已送出的內容,避免發生重複送出表單的情形發生。ok
可以簡單的得知response
回應的是不是200
(表示成功)。deleteArticleData('sync-posts',post.id)
透過此功能刪除資料庫中的內容。
在執行前,還必須做一件事情,必須先將firebase
上的資料庫改成可以寫入的規則。
現在我們可以來測試了!
從「Console」中,瀏覽器有支援因此會執行將資料存入IndexedDB
又因網路是順暢的,所以會直接執行sync
的內容,將表單送出去。
查看「Application/IndexedDB」,右鍵「Refresh」資料庫,sync-posts
會是空的,表示正確清除了已送出的表單。
重整畫面,資料即透過fetch
顯示出來了。
Firebase
顯示資料
拔掉網路線或關掉Wi-fi
填表單
點「發佈」並檢查sync-posts
插回網路線sync
偵測到網路觸發執行送發表單。
檢查Firebase
重整網頁
完工!